package by.gravity.billprinter.http;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyStore;
import java.util.List;
import java.util.zip.GZIPInputStream;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

import android.content.Context;
import android.util.Log;
import by.gravity.billprinter.data.SettingsManager;
import by.gravity.billprinter.http.cookie.CookieManager;
import by.gravity.billprinter.http.cookie.DefaultCookieStore;
import by.gravity.billprinter.utils.GetSystemService;

public class HttpClient {

	public static final String HTTP_CLIENT = "++HTTP_CLIENT++";

	private static final String TAG = HttpClient.class.getSimpleName();

	private static final String UTF_8 = "UTF-8";

	private static final int BUFFER_SIZE = 1024;

	private static final int SO_TIMEOUT = 60000;

	private DefaultHttpClient client;

	private static HttpClient instance;

	public static HttpClient get(Context context) {

		return (HttpClient) GetSystemService.get(context, HTTP_CLIENT);
	}

	public static final HttpClient newInstance(Context context) {

		instance = new HttpClient(context);
		return instance;
	}

	public static final HttpClient getInstance(Context context) {

		if (instance == null) {
			instance = new HttpClient(context);
		}

		return instance;
	}

	private HttpClient(Context context) {

		HttpParams params = new BasicHttpParams();

		HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
		HttpProtocolParams.setContentCharset(params, UTF_8);
		HttpConnectionParams.setConnectionTimeout(params, SO_TIMEOUT);
		HttpConnectionParams.setSoTimeout(params, SO_TIMEOUT);

		// REGISTERS SCHEMES FOR BOTH HTTP AND HTTPS
		SchemeRegistry registry = new SchemeRegistry();
		registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
		final SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
		sslSocketFactory.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		Integer port;
		try {
			port = Integer.valueOf(SettingsManager.getPort());
		} catch (NumberFormatException e) {
			port = 443;
		}
		registry.register(new Scheme("https", newSslSocketFactory(context), port));
		ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(params, registry);

		client = new DefaultHttpClient(manager, params);
		CookieManager cookieManager = new CookieManager(context);
		client.setCookieStore(new DefaultCookieStore(cookieManager));
	}

	private SSLSocketFactory newSslSocketFactory(Context context) {

		try {
			// Get an instance of the Bouncy Castle KeyStore format
			KeyStore trusted = KeyStore.getInstance(KeyStore.getDefaultType());

			// Get the raw resource, which contains the keystore with your trusted certificates (root and any intermediate certs)
			File file = new File(SettingsManager.getPathToCertificate());
			if (file.exists()) {
				InputStream in = new FileInputStream(file);
				try {
					// Initialize the keystore with the provided trusted certificates.
					// Also provide the password of the keystore
					trusted.load(in, SettingsManager.getCertificatePassword().toCharArray());
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					in.close();
				}
			}

			SSLSocketFactory sf = new SSLSocketFactory(trusted);

			sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
			return sf;
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}

	public String execute(HttpUriRequest request) throws ClientProtocolException, IOException {

		Log.d(TAG, request.getURI().toString());
		HttpResponse response = client.execute(request);
		InputStream instream = response.getEntity().getContent();
		Header contentEncoding = response.getFirstHeader("Content-Encoding");
		if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
			instream = new GZIPInputStream(instream);
		}
		String result = readString(instream);

		int status = response.getStatusLine().getStatusCode();

		if (status == HttpStatus.SC_OK) {
			Log.d(TAG, result);
			return result;
		} else {
			Log.d(TAG, "Http request status = " + status);
			Log.e(TAG, result);
			throw new IOException(result);
		}

	}

	public String post(final String url, String param, List<NameValuePair> headers) throws IOException {

		HttpPost request = new HttpPost(url);
		for (int i = 0; i < headers.size(); i++) {
			request.setHeader(headers.get(i).getName(), headers.get(i).getValue());
		}
		request.setEntity(new StringEntity(param, HTTP.UTF_8));
		Log.d(TAG, request.getRequestLine().toString());
		return execute(request);
	}

	public String loadAsString(String url) throws IOException {

		return execute(new HttpGet(url));
	}

	private String readString(InputStream is) throws IOException {

		if (is == null) {
			return null;
		}

		String text = null;
		BufferedReader reader = new BufferedReader(new InputStreamReader(is), BUFFER_SIZE);
		StringBuilder sb = new StringBuilder();
		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}
			text = sb.toString();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				Log.d(TAG, "IOException : " + e.getMessage());
			}
		}
		return text;
	}
}
